
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Spring Boot中实现多租户架构 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>Spring Boot中实现多租户架构 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">多租户架构的核心思想是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">多租户架构的核心思想是“资源共享和逻辑隔离”。它在一套硬件和软件资源的基础上，通过精细的逻辑设计，使多个租户能够安全、高效地使用同一应用。</div>
          </div>
          <div class="card-source">来源: 多租户架构概述 -> 核心思想</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">多租户架构常见的三种模式是什么？它们在隔离性和成本上有何关键区别？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. 独立数据库模式：每个租户拥有独立的数据库，数据完全隔离，成本最高。2. 表级隔离模式：各租户共享一个数据库实例，但每个租户有独立的表结构，数据隔离程度适中。3. 共享表模式：所有租户共享一个数据库和表，通过租户标识进行逻辑隔离，成本最低。</div>
          </div>
          <div class="card-source">来源: 多租户架构概述 -> 多租户的三种模式</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在多租户系统中，识别当前请求属于哪个租户的常见方式有哪些？（请列举至少三种）</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">常见的租户识别方式包括：1. 基于请求头（例如 `X-Tenant-ID`）；2. 基于子域名（例如 `tenant1.example.com`）；3. 基于路径参数（例如 `/tenant123/orders`）；4. 基于 Token 或 Cookie（将租户信息包含在认证凭证中）。</div>
          </div>
          <div class="card-source">来源: 租户识别机制 -> 2. 常见的租户识别方式</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在识别出租户后，如何确保在整个请求处理链中都能访问到正确的租户信息？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过“上下文传播”机制实现。通常将租户信息与当前线程绑定（例如使用 ThreadLocal），以确保后续的调用链（如微服务、数据库查询）能够获取到正确的租户上下文。</div>
          </div>
          <div class="card-source">来源: 租户识别机制 -> 3. 实现租户识别的关键点 -> 上下文传播</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">“共享表”模式的主要优缺点是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">优点：1. 资源高效，最大限度地节省硬件资源；2. 开发简单，统一的表结构减少开发复杂性；3. 扩展性好，适合租户数量多但数据量小的场景。缺点：1. 数据隔离性弱，依赖应用逻辑保证；2. 查询复杂度高，查询语句需要频繁加租户过滤条件；3. 随着数据量增长可能出现性能瓶颈。</div>
          </div>
          <div class="card-source">来源: 共享表的实现 -> 4. 共享表模式的优缺点</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在Spring Boot中，实现“独立数据库”模式下的动态数据源切换，通常会使用哪个关键技术？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通常使用Spring Boot的 `AbstractRoutingDataSource`。它可以根据从上下文中获取的租户标识，动态地切换到对应租户的数据源。</div>
          </div>
          <div class="card-source">来源: 数据库隔离的实现 -> 3. 数据库隔离机制中的关键技术</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">安全</div>
          <div class="card-question">在共享表模式下，为了保证数据安全，应用层应如何管理“租户上下文”？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">安全</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过线程局部变量（ThreadLocal）或类似机制在应用层存储当前请求的租户信息，形成“租户上下文”（Tenant Context）。这可以确保在多线程环境下每个请求的租户信息是隔离的，并在整个请求生命周期内保持一致性。</div>
          </div>
          <div class="card-source">来源: 安全性与隔离保障 -> 2.1 租户上下文管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">安全</div>
          <div class="card-question">除了在应用层代码中添加`tenant_id`过滤条件，还有什么数据库层面的机制可以增强共享表模式下的数据隔离性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">安全</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以使用数据库触发器（Database Triggers）。通过创建触发器，在数据插入或更新时自动验证 `tenant_id` 是否匹配当前租户上下文，从而在数据库层面防止跨租户的数据访问。</div>
          </div>
          <div class="card-source">来源: 安全性与隔离保障 -> 1.2 数据库触发器</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">优化</div>
          <div class="card-question">在共享表模式中，如何通过数据库设计来优化包含`tenant_id`的查询性能？（请列举两种方法）</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">优化</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. 索引优化：为租户标识字段（`tenant_id`）添加单独索引或组合索引，以提高查询速度。 2. 分区表：使用数据库的分区功能（如按`tenant_id`进行HASH分区），可以减少全表扫描，提升性能。</div>
          </div>
          <div class="card-source">来源: 共享表的实现 -> 2.4 索引优化, 3.1 分区表</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
